package websocket

import (
	"encoding/json"
	"time"

	"github.com/gorilla/websocket"
	"github.com/zeromicro/go-zero/core/logx"
)

const (
	// 允许的最大消息大小
	maxMessageSize = 512
	// 写入等待时间
	writeWait = 10 * time.Second
	// 心跳周期
	pingPeriod = (pongWait * 9) / 10
	// 读取pong等待时间
	pongWait = 60 * time.Second
)

// WritePump 客户端写入消息的循环
func (c *Client) WritePump() {
	pingTicker := time.NewTicker(pingPeriod)
	defer func() {
		pingTicker.Stop()
		c.mu.Lock()
		if c.Conn != nil {
			c.Conn.Close()
			c.Conn = nil
		}
		c.sendClosed = true
		c.mu.Unlock()
		logx.Infof("[WritePump] 用户 %s(%s) 写入循环结束", c.UserName, c.UserID)
	}()

	for {
		select {
		case message, ok := <-c.Send:
			if !ok {
				// 通道已关闭
				c.mu.Lock()
				if c.Conn != nil {
					c.Conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
				}
				c.mu.Unlock()
				return
			}

			c.mu.Lock()
			if c.Conn == nil {
				c.mu.Unlock()
				return
			}
			c.Conn.SetWriteDeadline(time.Now().Add(writeWait))
			c.mu.Unlock()

			// 直接发送消息
			c.mu.Lock()
			if c.Conn != nil {
				err := c.Conn.WriteMessage(websocket.TextMessage, message)
				if err != nil {
					logx.Errorf("[WritePump] 发送消息失败: %v", err)
					c.mu.Unlock()
					return
				}
			}
			c.mu.Unlock()

		case <-pingTicker.C:
			c.mu.Lock()
			if c.Conn == nil {
				c.mu.Unlock()
				return
			}
			c.Conn.SetWriteDeadline(time.Now().Add(writeWait))
			if err := c.Conn.WriteMessage(websocket.PingMessage, nil); err != nil {
				logx.Errorf("[WritePump] 发送ping失败: %v", err)
				c.mu.Unlock()
				return
			}
			c.mu.Unlock()
		}
	}
}

// ReadPump 客户端读取消息的循环
func (c *Client) ReadPump(room *Room) {
	defer func() {
		// 关闭发送通道，通知WritePump退出
		c.mu.Lock()
		if !c.sendClosed {
			close(c.Send)
			c.sendClosed = true
		}
		c.mu.Unlock()

		// 从房间移除客户端
		room.RemoveClient(c)

		// 用户下线 - 更新在线状态
		if c.server != nil {
			onlineStatusManager := c.server.GetOnlineStatusManager()
			if onlineStatusManager != nil {
				onlineStatusManager.UserOffline(c.UserID)
			}
		}

		logx.Infof("[ReadPump] 用户 %s(%s) 读取循环结束", c.UserName, c.UserID)
	}()

	c.Conn.SetReadLimit(maxMessageSize)
	c.Conn.SetReadDeadline(time.Now().Add(pongWait))
	c.Conn.SetPongHandler(func(string) error {
		c.Conn.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	// 创建消息处理器
	processor := NewMessageProcessor(c.server)

	for {
		_, message, err := c.Conn.ReadMessage()
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				logx.Errorf("[ReadPump] 读取消息错误: %v", err)
			}
			break
		}

		var msg Message
		if err := json.Unmarshal(message, &msg); err != nil {
			logx.Errorf("[ReadPump] 消息解析失败: %v", err)
			continue
		}

		msg.UserID = c.UserID
		msg.UserName = c.UserName
		msg.Time = time.Now().Unix()

		// 处理消息
		if err := processor.ProcessMessage(c, msg, room); err != nil {
			logx.Errorf("[ReadPump] 处理消息失败: %v", err)
		}
	}
}

// ReadPumpRoomList 客户端读取房间列表消息的循环
func (c *Client) ReadPumpRoomList() {
	defer func() {
		// 关闭发送通道，通知WritePump退出
		c.mu.Lock()
		if !c.sendClosed {
			close(c.Send)
			c.sendClosed = true
		}
		c.mu.Unlock()

		// 房间列表连接关闭时的清理工作
		logx.Infof("[ReadPumpRoomList] 用户 %s(%s) 断开房间列表连接", c.UserName, c.UserID)

		// 从房间列表客户端管理中移除
		if c.server != nil {
			c.server.roomListMu.Lock()
			delete(c.server.roomListClients, c.UserID)
			c.server.roomListMu.Unlock()
		}

		// 用户下线 - 更新在线状态
		if c.server != nil {
			onlineStatusManager := c.server.GetOnlineStatusManager()
			if onlineStatusManager != nil {
				onlineStatusManager.UserOffline(c.UserID)
			}
		}
	}()

	c.Conn.SetReadLimit(maxMessageSize)
	c.Conn.SetReadDeadline(time.Now().Add(pongWait))
	c.Conn.SetPongHandler(func(string) error {
		c.Conn.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	for {
		_, message, err := c.Conn.ReadMessage()
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				logx.Errorf("[ReadPumpRoomList] 读取消息错误: %v", err)
			}
			break
		}

		// 房间列表连接主要用于接收房间列表更新
		// 这里可以处理一些简单的消息，比如心跳
		var msg Message
		if err := json.Unmarshal(message, &msg); err != nil {
			logx.Errorf("[ReadPumpRoomList] 消息解析失败: %v", err)
			continue
		}

		// 处理房间列表相关的消息
		switch msg.Type {
		case int(MessageTypeSystem):
			// 系统消息，比如心跳
			logx.Infof("[ReadPumpRoomList] 收到系统消息: %s", msg.Content)
		default:
			// 其他消息类型，记录但不处理
			logx.Infof("[ReadPumpRoomList] 收到未知消息类型: %d", msg.Type)
		}
	}
}
